/*
 * Copyright (c) 2021 Talkweb Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <unistd.h>
#include "stdio.h"
#include "ohos_init.h"
#include "cmsis_os2.h"

#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "iot_i2c.h"
#include "iot_adc.h"
#include "battery_oledfont.h"

#define OLED_CMD 0	//写命令
#define OLED_DATA 1 //写数据
#define IOT_I2C_IDX_0 0
/**
 * @brief 定义I2C数据传输属性。 
 */
typedef struct {
    /** 指向存储要发送数据的缓冲区的指针  */
    unsigned char *sendBuf;
    /** 发送数据的长度 */
    unsigned int  sendLen;
    /** 指向存储要接收数据的缓冲区的指针  */
    unsigned char *receiveBuf;
    /** 接收数据的长度 */
    unsigned int  receiveLen;
} IotI2cData;
osThreadId_t g_oledTaskID = NULL;
unsigned char OLED_GRAM[144][8];
unsigned char g_send_data[2] = {0};

/**
 * @brief IIC写入1个字节
 * @param id: 对应的I2C组
 * @param device_addr: Oled的设备地址，这里默认是0X78
 * @param send_len: 写入的数据长度 
 */
unsigned int IICWrite(unsigned int id, unsigned short device_addr, unsigned int send_len)
{
	int status = 0;
	IotI2cData es8311_i2c_data = {0};

	es8311_i2c_data.sendBuf = g_send_data;
	es8311_i2c_data.sendLen = send_len;
	status = hi_i2c_write(id, device_addr, &es8311_i2c_data);
	if (status != IOT_SUCCESS)
	{
		printf("===== Error: I2C write status = %d! =====\r\n", status);
		return status;
	}
	return IOT_SUCCESS;
}

/**
 * @brief IIC写1字节命令
 * @param IIC_Command: 写入的命令值
 */
void IICWriteCommand(unsigned char IIC_Command)
{
	g_send_data[0] = 0x00;
	g_send_data[1] = IIC_Command;

	IICWrite(IOT_I2C_IDX_0, 0x78, 2);
}

/**
 * @brief IIC写1字节数据
 * @param IIC_Command: 写入的数据值
 */
void IICWriteData(unsigned char IIC_Data)
{
	g_send_data[0] = 0x40;
	g_send_data[1] = IIC_Data;

	IICWrite(IOT_I2C_IDX_0, 0x78, 2);
}

/**
 * @brief IIC写Oled函数，包括写命令和写数据
 * @param val: 写入的数值
 * @param type: 写入的类型:数据/命令
 */
void OledSendByte(unsigned char val, unsigned char type)
{
	if (type == OLED_DATA)
	{
		IICWriteData(val);
	}
	else
	{
		IICWriteCommand(val);
	}
}

/**
 * @brief 设置写入Oled的起始地址
 * @param x: 设置竖起始地址
 * @param y: 设置行起始地址
 */
void OledSendBP(unsigned char x, unsigned char y)
{
	OledSendByte(0xb0 + y, OLED_CMD); //设置行起始地址
	OledSendByte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
	OledSendByte((x & 0x0f), OLED_CMD);
}

/**
 * @brief 开启Oled显示
 */
void OledShowON(void)
{
	OledSendByte(0x8D, OLED_CMD); //电荷泵使能
	OledSendByte(0x14, OLED_CMD); //开启电荷泵
	OledSendByte(0xAF, OLED_CMD); //点亮屏幕
}

/**
 * @brief 关闭Oled显示
 */
void OledShowOFF(void)
{
	OledSendByte(0x8D, OLED_CMD); //电荷泵使能
	OledSendByte(0x10, OLED_CMD); //关闭电荷泵
	OledSendByte(0xAF, OLED_CMD); //关闭屏幕
}

/**
 * @brief 更新Oled显示的屏
 */
void OledRefresh(void)
{
	unsigned char i = 0, n = 0;

	for (i = 0; i < 8; i++)
	{
		OledSendByte(0xb0 + i, OLED_CMD); //设置行起始地址
		OledSendByte(0x00, OLED_CMD);	  //设置低列起始地址
		OledSendByte(0x10, OLED_CMD);	  //设置高列起始地址
		for (n = 0; n < 128; n++)
		{
			OledSendByte(OLED_GRAM[n][i], OLED_DATA);
		}
	}
}

/**
 * @brief 清除Oled屏幕
 */
void OledClear(void)
{
	unsigned char i = 0, n = 0;
	for (i = 0; i < 8; i++)
	{
		for (n = 0; n < 128; n++)
		{
			OLED_GRAM[n][i] = 0; //清除所有数据
		}
	}
	OledRefresh(); //更新显示
}

/**
 * @brief Oled画点函数
 * @param x: 画点的竖地址，0~127
 * @param y: 画点的行地址，0~63
 */
void OledDrawPoint(unsigned char x, unsigned char y)
{
	unsigned char i = 0, m = 0, n = 0;

	i = y / 8;
	m = y % 8;
	n = 1 << m;
	OLED_GRAM[x][i] |= n;
}

/**
 * @brief Oled清除一个点
 * @param x: 点的竖地址，0~127
 * @param y: 点的行地址，0~63
 */
void OledClearPoint(unsigned char x, unsigned char y)
{
	unsigned char i = 0, m = 0, n = 0;

	i = y / 8;
	m = y % 8;
	n = 1 << m;
	OLED_GRAM[x][i] = ~OLED_GRAM[x][i];
	OLED_GRAM[x][i] |= n;
	OLED_GRAM[x][i] = ~OLED_GRAM[x][i];
}

/**
 * @brief 在指定位置显示一个字符,包括部分字符(取模方式 逐列式)
 * @param x: 字符的竖地址，0~127
 * @param y: 字符的行地址，0~63
 * @param chr: 显示的字符值
 * @param size1: 字符显示字体:12/16/24
 */
void OledShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size1)
{
	unsigned char i = 0, m = 0;
	unsigned char temp = 0, size2 = 0, chr1 = 0;
	unsigned char y0 = y;
	size2 = (size1 / 8 + ((size1 % 8) ? 1 : 0)) * (size1 / 2); //得到字体一个字符对应点阵集所占的字节数
	chr1 = chr - ' ';										   //计算偏移后的值

	for (i = 0; i < size2; i++)
	{
		if (size1 == 12) //调用1206字体
		{
			temp = asc2_1206[chr1][i];
		}
		else if (size1 == 16) //调用1608字体
		{
			temp = asc2_1608[chr1][i];
		}
		else
		{
			return;
		}

		for (m = 0; m < 8; m++) //写入数据
		{
			if (temp & 0x80)
			{
				OledDrawPoint(x, y);
			}
			else
			{
				OledClearPoint(x, y);
			}
			temp <<= 1;
			y++;
			if ((y - y0) == size1)
			{
				y = y0;
				x++;
				break;
			}
		}
	}
}

/**
 * @brief 在指定位置显示字符串
 * @param x: 字符的竖地址，0~127
 * @param y: 字符的行地址，0~63
 * @param chr: 显示的字符串
 * @param size1: 字符显示字体:12/16/24
 */
void OledShowString(unsigned char x, unsigned char y, char *chr, unsigned char size1)
{
	while ((*chr >= ' ') && (*chr <= '~')) //判断是不是非法字符!
	{
		OledShowChar(x, y, *chr, size1);
		x += size1 / 2;
		if (x > 128 - size1) //换行
		{
			x = 0;
			y += 2;
		}
		chr++;
	}
}

/**
 * @brief Oled初始化函数
 */
void OledInit(void)
{
	OledSendByte(0xAE, OLED_CMD); //--turn off oled panel  #
	OledSendByte(0x00, OLED_CMD); //---set low column address #
	OledSendByte(0x10, OLED_CMD); //---set high column address #
	OledSendByte(0x40, OLED_CMD); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F) #
	OledSendByte(0x81, OLED_CMD); //--set contrast control register #
	OledSendByte(0xCF, OLED_CMD); // Set SEG Output Current Brightness #
	OledSendByte(0xA1, OLED_CMD); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常 #
	OledSendByte(0xC8, OLED_CMD); //Set COM/Row Scan Direction   0xc0上下反置 0xc8正常 #
	OledSendByte(0xA6, OLED_CMD); //--set normal display
	OledSendByte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64) #
	OledSendByte(0x3f, OLED_CMD); //--1/64 duty #
	OledSendByte(0xD3, OLED_CMD); //-set display offset	Shift Mapping RAM Counter (0x00~0x3F) #
	OledSendByte(0x00, OLED_CMD); //-not offset #
	OledSendByte(0xd5, OLED_CMD); //--set display clock divide ratio/oscillator frequency #
	OledSendByte(0x80, OLED_CMD); //--set divide ratio, Set Clock as 100 Frames/Sec #
	OledSendByte(0xD9, OLED_CMD); //--set pre-charge period #
	OledSendByte(0xF1, OLED_CMD); //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock #
	OledSendByte(0xDA, OLED_CMD); //--set com pins hardware configuration #
	OledSendByte(0x12, OLED_CMD); // #
	OledSendByte(0xDB, OLED_CMD); //--set vcomh #
	OledSendByte(0x40, OLED_CMD); //Set VCOM Deselect Level #
	OledSendByte(0x20, OLED_CMD); //-Set Page Addressing Mode (0x00/0x01/0x02) #
	OledSendByte(0x02, OLED_CMD); //
	OledSendByte(0x8D, OLED_CMD); //--set Charge Pump enable/disable #
	OledSendByte(0x14, OLED_CMD); //--set(0x10) disable #
	OledSendByte(0xA4, OLED_CMD); // Disable Entire Display On (0xa4/0xa5) #
	OledSendByte(0xA6, OLED_CMD); // Disable Inverse Display On (0xa6/a7)  #
	OledSendByte(0xAF, OLED_CMD); // #
	OledClear();
}

/**
 * @brief IIC配置及Oled初始化函数
 */
void OledIICInit(void)
{
	//初始化I2C0 配置，速率为400K
	IoTGpioInit(IOT_IO_NAME_GPIO_13);
	IoTGpioInit(IOT_IO_NAME_GPIO_14);

	IoTIoSetPull(IOT_IO_NAME_GPIO_13, IOT_IO_PULL_UP);
	IoTIoSetPull(IOT_IO_NAME_GPIO_14, IOT_IO_PULL_UP);

	IoTIoSetFunc(IOT_IO_NAME_GPIO_13, IOT_IO_FUNC_GPIO_13_I2C0_SDA);
	IoTIoSetFunc(IOT_IO_NAME_GPIO_14, IOT_IO_FUNC_GPIO_14_I2C0_SCL);

	IoTI2cInit(IOT_I2C_IDX_0, 400 * 1000);

	OledInit();
}
